
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.util.Properties;


/**
 * This class represents the main controller of the user agent. The function of this agent
 * is to transmit measures received from an electrical power usage measurement instrument.
 * <p>
 * These measures are captured in the MeasureThread, connected with an RS-232 interface.
 * Those measures are transmitted to the CommunicationThread, which will retransmit them to
 * a remote server, and will then be stored in a database.<p>
 *
 * If the server crashes after sending measures, those are kept in memory and will be erased only
 * when the server confirms the reception of these measures.
 * @author Alexandre Dos Santos Coelho
 */
public class AgentController {

  private String machineName; // local machine hostname
  private String machineType; // Type of local machine (model)
  private String userName; // logged user
  private MeasureThread measureThread; // measure thread which will receive measure from the device
  private CommunicationThread communicationThread; // thread that will send measures to the remote server
  private String serverConfFile = "config.conf";
  private boolean connected = false;

  /**
   * Contructor. Starts the agent.
   */
  public AgentController() {
    loadConfig();
    
    // Executing script to get machine name
    Runtime runtime = Runtime.getRuntime();
    try {
      String path = new File(".").getCanonicalPath() + File.separator + "machineType.vbs";
      path.replace("\\", "\\\\");
      final Process process = runtime.exec("wscript \""+ path+"\"");

      try { // Delaying text file lecture
        Thread.sleep(10000);
      } catch (InterruptedException ex) {}

      BufferedReader in = new BufferedReader(new FileReader("machineType.txt"));
      machineType = in.readLine().trim(); // Reading machine name (on first line)

      in = new BufferedReader(new FileReader("userName.txt"));
      userName = in.readLine().trim().split("\\\\")[1]; // Reading current logged user, without domain
      in.close();

      machineName = InetAddress.getLocalHost().getHostName().split(".local")[0];

    } catch (IOException ex) {
      System.err.println(Logger.getTime() + "Error: " + ex.getMessage());
    }

    communicationThread = new CommunicationThread(this); // Starting communicationThread
    measureThread = new MeasureThread(this); // Starting measureThread
  }

  /**
   * This method is used by the MeasureThread to transmit captured measures to the remote
   * server. This will be done by adding the measure in the CommunicationThread's sending queue
   * @param m Measure to transmit to the CommunicationThread
   */
  public void transmitMeasure(Measure m) {
    communicationThread.addMeasure(m);
  }

  /**
   * Creates (and thus starts) an instance of AgentController.
   */
  public static void main(String... args) {
    new AgentController();
  }

  /**
   * @return Machine hostname
   */
  public String getMachineName() {
    return machineName;
  }

  /**
   * @return Machine logged username
   */
  public String getUserName() {
    return userName;
  }

  /**
   * Checks if the "client.conf" file exists. If not, it will be downloaded by the URL
   * specified in the file "config.conf"
   */
  private void loadConfig() {
    System.out.println(Logger.getTime() + "Downloading configuration file...");
    Properties config = new Properties();

    // Open configuration file
    try {
      config.load(new FileInputStream(serverConfFile));
    } catch (IOException ex) {
      System.err.println(Logger.getTime() + "Impossible to open configuration file!");
    }

    String address = (String) config.get("configFile"); // Get configuration file URL
    MeasureThread.portName = (String) config.get("comPort");
    OutputStream out = null; // Stream to write in the configuration file
    URLConnection conn = null; // Connection to the remote configuration file
    InputStream in = null; // Stream getting remote configuration file

    try {
      URL url = new URL(address);
      out = new BufferedOutputStream(
              new FileOutputStream("client.conf")); // Creating configuration file
      conn = url.openConnection(); // Opening remote connection to server
      in = conn.getInputStream(); // Getting stream on remote configuration file
      byte[] buffer = new byte[1024]; // Buffer in which we put remote config file characters
      int nbCharsRead; // Number of characters read from remote config file
      while ((nbCharsRead = in.read(buffer)) != -1) { // Reading remote file until EOF
        out.write(buffer, 0, nbCharsRead); // Writing local configuration file
      }
    } catch (IOException ex) {
      System.err.println(Logger.getTime() + "IOException: " + ex.getMessage());
    } finally {
      try {
        if (in != null) {
          in.close(); // Closing remote stream
        }
        if (out != null) {
          out.close(); // Closing and saving local file
        }
      } catch (IOException ioe) {}
    }

    System.out.println(Logger.getTime() + "Configuration file loaded, launching agent.");

  }

  /**
   * @return the isConnected
   */
  public boolean isConnected() {
    return connected;
  }

  /**
   * @param isConnected the isConnected to set
   */
  public void setConnected(boolean isConnected) {
    this.connected = isConnected;
  }

  /**
   * @return the machineType
   */
  public String getMachineType() {
    return machineType;
  }

  /**
   * @param machineType the machineType to set
   */
  public void setMachineType(String machineType) {
    this.machineType = machineType;
  }
}
